﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.CMS.Workflow;
using MethodWorx.Core.DomainModel;
using MethodWorx.CMS.Providers;
using MethodWorx.CMS.Membership;

namespace MethodWorx.CMS.TemplateParser.Installer
{
    public class InstallerParser
    {
        private List<string> errors = new List<string>();
        private Dictionary<string, byte[]> files;
        private User user;

        public InstallerParser(Dictionary<string, byte[]> files, User user)
        {
            if(!files.ContainsKey("installer.xml"))
                errors.Add("Missing installer.xml");
            else
            {
                this.user = user;
                this.files = files;
                this.ParseInstallerXml();
            }
        }

        public string Name { get; private set; }
        public string Author { get; private set; }
        public byte[] Preview { get; private set; }
        public string Complete { get; private set; }


        private void ParseInstallerXml()
        {
            //  parser here
            //this.InstallerType = InstallerType.MasterTemplate;
            var xmlParser = new XmlParser(System.Text.ASCIIEncoding.ASCII.GetString(files["installer.xml"]));
            var node = xmlParser.Document.FindElements("template", true).FirstOrDefault();
            bool found = false;
            if(node != null)
            {
                var type = node.GetAttributeValue("type", "", true);
                switch(type.ToLower())
                {
                    case "masterpagetemplate":
                        this.InstallerType = InstallerType.MasterTemplate;
                        found = true;
                        break;
                    case "macro":
                        this.InstallerType = InstallerType.MasterTemplate;
                        found = true;
                        break;
                    default:
                        this.errors.Add("Unknown installer type: '" + type + "'");
                        break;
                }
            }

            if (found)
            {
                this.ParseInformation(node);
                this.ParseWizards(node);
                this.ParseWorkflow(node);
                this.ParseComplete(node);
            }
        }

        private void ParseComplete(TemplateElement node)
        {
            try
            {
                this.Complete = node.GetElement("Complete").InnerXml;
            }
            catch (Exception ex)
            {
                
            }
        }

        private void ParseInformation(TemplateElement node)
        {
            try
            {
                this.Name = node.GetElement("Information").GetElement("Name").InnerXml;
                this.Author = node.GetElement("Information").GetElement("Author").InnerXml;

                var preview = node.GetElement("Information").GetElement("Preview").GetAttributeValue("content", "", true);
                if (this.files.ContainsKey(preview.ToLower()))
                    this.Preview = this.files[preview.ToLower()];

            }
            catch (Exception ex)
            {
                
            }
            
        }

        private void ParseWorkflow(TemplateElement node)
        {
            List<WorkflowStep> steps = new List<WorkflowStep>();
            var workflow = node.GetElement("Workflow");
            if(workflow != null)
            {
                foreach(var step in workflow.Elements)
                {
                    var ws = new WorkflowStep();
                    ws.Name = step.Name;
                    ws.Configuration = step;
                    ws.Output = step.GetAttributeValue("output", null, true);
                    steps.Add(ws);
                }
            }
            this.WorkflowSteps = steps.ToArray();
        }

        private void ParseWizards(TemplateElement element)
        {
            List<WizardStep> steps = new List<WizardStep>();
            var wizard = element.GetElement("Wizard");
            if(wizard != null)
            {
                foreach(var step in wizard.GetElements("Step"))
                {
                    var ws = new WizardStep();
                    var intro = step.GetElement("Intro");
                    if (intro != null)
                        ws.Intro = intro.InnerXml;

                    this.ParseWizardSteps(step, ws);

                    steps.Add(ws);
                    
                }
            }

            this.WizardSteps = steps.ToArray();
        }

        private void ParseWizardSteps(TemplateElement step, WizardStep ws)
        {
            List<WizardPrompt> prompts = new List<WizardPrompt>();
            foreach(var ask in step.GetElements("Ask"))
            {
                var prompt = new WizardPrompt();
                prompt.Value = ask.GetAttributeValue("default", "", true);
                prompt.Variable = ask.GetAttributeValue("var", "", true);
                prompt.Type = ask.GetAttributeValue("type", "textbox", true);
                prompt.Prompt = ask.InnerXml;
                prompt.AddParametersFromTemplateElement(ask);
                prompts.Add(prompt);
            }
            ws.Prompts = prompts.ToArray();
        }

        public IEnumerable<string> Errors
        {
            get { return errors.AsReadOnly();  }
        }

        public InstallerType InstallerType { get; private set; }
        public WizardStep[] WizardSteps { get; private set; }
        public WorkflowStep[] WorkflowSteps { get; private set; }

        public WorkflowExecutionEngine GetWorkflowExecutionEngine(ICmsDataProvider provider)
        {
            List<Type> types = new List<Type>();

            foreach(var type in this.GetType().Assembly.GetTypes())
            {
                if(!type.IsAbstract && type.Namespace.EndsWith(".Components") && typeof(IInstallationWorkflowComponent).IsAssignableFrom(type))
                {
                    //
                    //  this is an installation workflow component
                    //  so lets sort it out
                    types.Add(type);
                }
            }

            //
            //  now we need to sort out the configuration
            WorkflowConfiguration workflow = new WorkflowConfiguration();
            WorkflowConfigurationStep currentStep = null;

            foreach(var step in this.WorkflowSteps)
            {
                var type = types.FirstOrDefault(t => t.Name.ToLower() == step.Name.ToLower());
                if(type != null)
                {
                    //
                    //  ok, lets sort it all out
                    var newStep = new WorkflowConfigurationStep();
                    newStep.ComponentId = new Guid("{65EFDBDB-1F1C-41CC-8642-ED8F5C9406B1}");
                    newStep.ComponentConfiguration = new InstallerWorkflowComponentConfiguration((IInstallationWorkflowComponent)Activator.CreateInstance(type))
                                                         {
                                                             Files = this.files,
                                                             ConfigurationElement = step.Configuration
                                                         };
                    newStep.Output = step.Output;

                    if(currentStep == null)
                        workflow.StartingStep = newStep;
                    else
                    {
                        currentStep.Steps = new WorkflowConfigurationStep[] {newStep};
                    }
                    currentStep = newStep;
                }
                else
                    throw new Exception("Unknown installation workflow component: " + step.Name);
            }

            var engine =
                new WorkflowExecutionEngine(new IWorkflowComponent[] {new InstallerWorfklowComponent()},
                                            new InstallerWorkflowExecutionContext(provider, this, this.user), workflow);
            return engine;
        }
    }
}
